package com.macro.mall.es.service.impl;

import com.alibaba.fastjson2.JSON;
import com.macro.mall.common.api.*;
import com.macro.mall.es.service.EsDocService;
import com.macro.mall.es.service.EsIndexService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @Author：Minxiao_Hao
 * @Description:
 * @name：EsServiceImpl
 * @Date：2024/10/15 20:39
 */
@Service
@Slf4j
public class EsDocServiceImpl implements EsDocService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;
    @Autowired
    private EsIndexService esIndexService;

    /**
     * 批量添加
     *
     * @param esDocDto 文档信息
     * @return
     */
    @Override
    public void batchAdd(EsDocDto esDocDto) {
        // 检查索引是否存在
        if (!esIndexService.exists(esDocDto.getIndexName())) {
            throw new RuntimeException("索引不存在");
        }

        List<Map<String, Object>> dataS = esDocDto.getDataS();
        BulkRequest bulkRequest = new BulkRequest();

        // 准备添加请求
        for (Map<String, Object> data : dataS) {
            String id =  data.get(esDocDto.getDocId()).toString(); // 获取每个文档的ID
            if(id!=null){
                bulkRequest.add(new IndexRequest(esDocDto.getIndexName())
                        .id(id)
                        .source(data));
            }else{
                bulkRequest.add(new IndexRequest(esDocDto.getIndexName())
                        .source(data));
            }
        }
        //执行批量添加
        if (bulkRequest.numberOfActions() > 0) {
            try {
                BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
                // 检查是否有失败
                if (bulkResponse.hasFailures()) {
                    log.error("批量添加失败: {}", bulkResponse.buildFailureMessage());

                    // 获取所有批量操作的结果
                    BulkItemResponse[] items = bulkResponse.getItems();

                    // 收集失败的文档 ID
                    List<String> failedIds = new ArrayList<>();
                    for (BulkItemResponse item : items) {
                        if (!item.isFailed()) {
                            String failedId = item.getId(); // 获取失败项的 ID
                            failedIds.add(failedId);
                        }
                    }

                    // 如果有失败的文档 ID，执行删除操作
                    if (!failedIds.isEmpty()) {
                        log.info("开始删除失败的文档: {}", failedIds);
                        batchDelete(esDocDto.getIndexName(), failedIds);  // 批量删除失败的文档
                    }

                    throw new RuntimeException("批量添加失败,请重新试一下");
                } else {
                    log.info("批量添加成功");
                }
            } catch (Exception e) {
                log.error("ES批量添加异常", e);
            }
        } else {
            log.warn("批量添加数据为空");
        }
    }

    /**
     * 批量删除
     *
     * @param indexName 索引名称
     * @param ids       多个id
     */
    @Override
    public void batchDelete(String indexName, List<String> ids) {
        // 检查索引是否存在
        if (!esIndexService.exists(indexName)) {
            throw new RuntimeException("索引不存在: " + indexName);
        }

        // 创建一个 BulkRequest 用于批量删除
        BulkRequest bulkRequest = new BulkRequest();
        log.info("开始从索引 " + indexName + " 批量删除文档: " + ids);

        for (String id : ids) {
            DeleteRequest deleteRequest = new DeleteRequest(indexName, id);
            bulkRequest.add(deleteRequest); // 将删除请求添加到批量请求中
        }

        // 执行批量请求
        try {
            BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
            if (bulkResponse.hasFailures()) {
                log.error("批量删除失败: " + bulkResponse.buildFailureMessage());
                List<String> failedIds = new ArrayList<>();
                for (BulkItemResponse itemResponse : bulkResponse) {
                    if (itemResponse.isFailed()) {
                        failedIds.add(itemResponse.getId());
                    }
                }
                log.error("失败的 ID 列表: " + failedIds);
            } else {
                log.info("成功从索引 " + indexName + " 删除了 " + ids.size() + " 个文档");
            }
        } catch (IOException e) {
            log.error("删除文档时发生错误: " + e.getMessage());
            e.printStackTrace();
        }
    }




    /**
     * 查询接口, 支持分页、排序
     * EsQuery esQuery
     * @return
     */

    @Override
    public EsResult basicQuery(EsQuery esQuery) {

        // 检查索引是否存在
        if (!esIndexService.exists(esQuery.getIndexName())) {
            log.info("索引不存在: " + esQuery.getIndexName());
        }

        //搜索提交对象
        SearchRequest searchRequest = new SearchRequest(esQuery.getIndexName());
        //条件构造器
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        OrderInfo orderInfo = esQuery.getOrderInfo();
        PageInfo pageInfo = esQuery.getPageInfo();
        EsResult results = getObjects(searchRequest, searchSourceBuilder, orderInfo, pageInfo);
        return results;
    }

    /**
     * 查询接口, 支持分页、排序、可以支持设置多属性进行全文检索
     * @param esQuery 查询参数
     * @return 查询结果
     */
    @Override
    public EsResult advancedQuery(EsQuery esQuery) {
        // 检查指定的索引是否存在，如果不存在则记录日志信息
        // 检查索引是否存在
        if (!esIndexService.exists(esQuery.getIndexName())) {
            throw new RuntimeException("索引不存在: " + esQuery.getIndexName());
        }

        // 创建搜索请求对象，指定要查询的索引名称
        SearchRequest searchRequest = new SearchRequest(esQuery.getIndexName());

        // 创建条件构造器，用于构建查询条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 获取匹配查询信息
        matchQueryInfo matchQueryInfo = esQuery.getMatchQueryInfo();
        String keyWord = matchQueryInfo.getKeyWord(); // 获取查询的关键词
        List<String> searchFields = matchQueryInfo.getSearchFields(); // 获取要搜索的字段列表

        // 检查搜索字段是否不为空，并为每个搜索字段构建多字段匹配查询
        if (searchFields != null) {
            for (String searchField : searchFields) {
                if (StringUtils.isNotBlank(searchField)) {
                    // 使用多字段匹配查询，添加到条件构造器中
                    boolQueryBuilder.should(QueryBuilders.matchQuery(searchField, keyWord));
                }
            }
        }
        searchSourceBuilder.query(boolQueryBuilder);
        // 获取排序信息和分页信息
        OrderInfo orderInfo = esQuery.getOrderInfo(); // 获取排序信息
        PageInfo pageInfo = esQuery.getPageInfo(); // 获取分页信息

        // 调用方法获取最终的查询结果
        EsResult results = getObjects(searchRequest, searchSourceBuilder, orderInfo, pageInfo);
        return results; // 返回查询结果列表
    }

    /**
     * 根据 SearchResult 查询 Elasticsearch
     *
     * @param searchResult 查询参数对象
     * @return 符合条件的内容列表
     * @throws IOException 如果发生错误
     */
    @Override
    public List<String> searchByPinyin(SearchResult searchResult)  {
        List<String> results = new ArrayList<>();

        // 创建搜索请求
        SearchRequest searchRequest = new SearchRequest(searchResult.getIndexName());

        // 构建查询
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

        // 如果支持的字段不为空，添加字段查询
        if (searchResult.getSuggestFields() != null && !searchResult.getSuggestFields().isEmpty()) {
            for (String field : searchResult.getSuggestFields()) {
                // 对每个字段进行模糊匹配
                boolQuery.should(QueryBuilders.matchQuery(field, searchResult.getPinyin()));
            }
        }

        // 将查询添加到搜索请求中
        searchRequest.source().query(boolQuery);

        // 执行搜索
        SearchResponse response = null;
        try {
            response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            // 处理搜索结果
            for (SearchHit hit : response.getHits().getHits()) {
                String content = (String) hit.getSourceAsMap().get("content"); // 假设内容字段为 "content"
                if (content != null) {
                    results.add(content);
                }
            }
        } catch (IOException e) {
            log.error("搜索失败: " + e.getMessage());
        }
        return results;
    }


    /**
     * 处理查询结果和查询条件
     * @param searchRequest
     * @param searchSourceBuilder
     * @param orderInfo
     * @param pageInfo
     * @return
     */
    private EsResult getObjects(SearchRequest searchRequest, SearchSourceBuilder searchSourceBuilder, OrderInfo orderInfo, PageInfo pageInfo) {
        EsResult esResult = new EsResult();
        //设置分页条件(可活可死)
        if (pageInfo != null && pageInfo.getNum() != null && pageInfo.getSize() != null && pageInfo.getNum() > 0 && pageInfo.getSize() > 0) {
            searchSourceBuilder.from((pageInfo.getNum() - 1) * pageInfo.getSize()).size(pageInfo.getSize());
        }
        //排序
        if (orderInfo != null &&  Strings.isNotBlank(orderInfo.getOrderField()) && Strings.isNotBlank(orderInfo.getOrderType())) {
            searchSourceBuilder.sort(orderInfo.getOrderField(), SortOrder.fromString(orderInfo.getOrderType()));
        }

        searchRequest.source(searchSourceBuilder);
        // 发送请求并处理响应
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            // 处理响应
            List<Object> results = new ArrayList<>();
            for (SearchHit hit : searchResponse.getHits().getHits()) {
                // 这里可以根据具体需求解析数据，例如将其转换为某个特定的对象
                String sourceAsString = hit.getSourceAsString();
                //反序列化
                Object object = JSON.parseObject(sourceAsString);
                results.add(object); // 或者根据需要转换为其他类型
            }
            esResult.setData(results);
            esResult.setTotal(searchResponse.getHits().getTotalHits().value);
            return esResult;
        } catch (IOException e) {
            log.error("查询文档时发生错误: " + e.getMessage());
        }
        return null;
    }
}
